package com.cskaoyan.wordmemorize.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cskaoyan.wordmemorize.auth.StpKit;
import com.cskaoyan.wordmemorize.common.exception.BusinessException;
import com.cskaoyan.wordmemorize.converter.UserCheckinConverter;
import com.cskaoyan.wordmemorize.dao.entity.*;
import com.cskaoyan.wordmemorize.dao.mapper.*;
import com.cskaoyan.wordmemorize.dto.admin.PageDTO;
import com.cskaoyan.wordmemorize.dto.app.UserCheckinDTO;
import com.cskaoyan.wordmemorize.dto.app.UserCheckinStatisticsDTO;
import com.cskaoyan.wordmemorize.request.PageRequest;
import com.cskaoyan.wordmemorize.request.UserCheckinCommand;
import com.cskaoyan.wordmemorize.service.UserCheckinService;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;

@Service
public class UserCheckinServiceImpl implements UserCheckinService {
    @Resource
    UserCheckinMapper userCheckinMapper;
    @Resource
    UserCheckinStatisticsMapper userCheckinStatisticsMapper;
    @Resource
    UserVocBoundLogMapper userVocBoundLogMapper;
    @Resource
    UserVocBoundStatisticsMapper userVocBoundStatisticsMapper;
    @Resource
    LevelMapper levelMapper;
    @Resource
    UserMapper userMapper;
    @Resource
    UserCheckinConverter userCheckinConverter;

    // APP端下载打卡数据(如果分页参数没有传的话，就返回所有打卡数据)
    @Override
    public PageDTO<UserCheckinDTO> getCheckInData(PageRequest pageRequest, Long userId) {
        Page<UserCheckinDO> page = new Page<>(pageRequest.getPageNum(), pageRequest.getCount());
        LambdaQueryWrapper<UserCheckinDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserCheckinDO::getUserId, userId);
        Page<UserCheckinDO> userCheckinDOPage = userCheckinMapper.selectPage(page, queryWrapper);
        return userCheckinConverter.levelDOToPageDTO(userCheckinDOPage.getRecords(), userCheckinDOPage.getTotal());
    }

    // APP端打卡
    @Override
    @Transactional
    public UserCheckinStatisticsDTO checkin(UserCheckinCommand command) {
        UserCheckinDO userCheckinDO = userCheckinConverter.userCheckinCommandToDO(command);
        // 获得用户id
        Long userId = userCheckinDO.getUserId();
        UserCheckinDO checkin = new UserCheckinDO();
        // 声明段号
        Long stickNo;
        // 获取当天日期
        LocalDate today = LocalDate.now();
        // 条件构造器
        LambdaQueryWrapper<UserCheckinDO> checkinQueryWrapper = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<UserCheckinStatisticsDO> checkinStatisticsQueryWrapper = new LambdaQueryWrapper<>();
        LambdaUpdateWrapper<UserCheckinStatisticsDO> checkinStatisticsUpdateWrapper = new LambdaUpdateWrapper<>();
        // 判断今天是否签到
        if (userCheckinMapper.exists(
                new LambdaQueryWrapper<UserCheckinDO>()
                        .eq(UserCheckinDO::getUserId, userId)
                        .eq(UserCheckinDO::getSignDate, today)
        )) {
            throw new BusinessException("打卡日期重复",6017);
        }
            // 获取最近一次签到
            checkinQueryWrapper.eq(UserCheckinDO::getUserId, userId)
                    .orderByDesc(UserCheckinDO::getSignDate)
                    .last("Limit 1");
            UserCheckinDO latestCheckin = userCheckinMapper.selectOne(checkinQueryWrapper);
            if (latestCheckin==null){
                stickNo=1L;
                checkin.setSignDate(today);
                checkin.setStickNo(stickNo);
                checkin.setType(1);

                latestCheckin = checkin;
            }
            // 判断是否连续签到
            long daysBetween = ChronoUnit.DAYS.between(latestCheckin.getSignDate(), today);
            stickNo = daysBetween > 1 ? latestCheckin.getStickNo() + 1 : latestCheckin.getStickNo();
            // 插入签到数据
            checkin.setUserId(userId);
            checkin.setStickNo(stickNo);
            checkin.setSignDate(today);
            checkin.setType(1);
            userCheckinMapper.insert(checkin);
            // statistics 表
            // 根据用户id来查询出签到数据
            checkinStatisticsQueryWrapper.eq(UserCheckinStatisticsDO::getUserId, userId);
            UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectOne(checkinStatisticsQueryWrapper);

            Integer totalDays = userCheckinStatisticsDO.getTotalDays();
            Integer curContinuousDays = userCheckinStatisticsDO.getCurContinuousDays();
            Integer maxContinuousDays = userCheckinStatisticsDO.getMaxContinuousDays();
            LocalDate curContinuousStartDate = userCheckinStatisticsDO.getCurContinuousStartDate();
            LocalDate maxContinuousStartDate = userCheckinStatisticsDO.getMaxContinuousStartDate();
            // 如果是连签，当前连签天数+1，否则设置当前连签为1，日期为today
            // 如果当前连签大于历史最大，更新最大天数和历史开始日期
            if (daysBetween==1){
                curContinuousDays+=1;
                if (curContinuousDays>maxContinuousDays){
                    maxContinuousDays=curContinuousDays;
                    maxContinuousStartDate=curContinuousStartDate;
                }
            }else {
                curContinuousDays=1;
                curContinuousStartDate=today;
                if (curContinuousDays>maxContinuousDays){
                    maxContinuousDays=curContinuousDays;
                    maxContinuousStartDate=curContinuousStartDate;
                }
            }

            // 更新statistics表
            checkinStatisticsUpdateWrapper.eq(UserCheckinStatisticsDO::getUserId, userId)
                    .set(UserCheckinStatisticsDO::getTotalDays, totalDays + 1)
                    .set(UserCheckinStatisticsDO::getCurContinuousDays,  curContinuousDays )
                    .set(UserCheckinStatisticsDO::getMaxContinuousDays,  maxContinuousDays)
                    .set(UserCheckinStatisticsDO::getCurContinuousStartDate,curContinuousStartDate)
                    .set(UserCheckinStatisticsDO::getMaxContinuousStartDate, maxContinuousStartDate);
            userCheckinStatisticsMapper.update(null, checkinStatisticsUpdateWrapper);
            UserCheckinStatisticsDO updatedCheckinStatistics = userCheckinStatisticsMapper.selectOne( new LambdaQueryWrapper<UserCheckinStatisticsDO>()
                    .eq(UserCheckinStatisticsDO::getUserId, userId));
        // 更新单词上限
        updateVocLimit( userId);
        // 添加单词上限变更日志
        insertVocLimitUpdatedLog(userId, today);
        // 更新用户等级
        updateUserLevel(userId);

        return userCheckinConverter.statisticsDoToDto(updatedCheckinStatistics);


    }
    // 添加单词上限变更日志
   public void insertVocLimitUpdatedLog(Long userId, LocalDate today) {
        UserVocBoundLogDO userVocBoundLogDO = new UserVocBoundLogDO();
        userVocBoundLogDO.setUserId(userId);
        userVocBoundLogDO.setLogDate(today);
        userVocBoundLogDO.setType(2);
        userVocBoundLogDO.setCount(1);
        userVocBoundLogDO.setDescription("签到");
        userVocBoundLogMapper.insert(userVocBoundLogDO);
    }

    // 更新单词上限
    @Override
    public  void updateVocLimit( Long userId) {
        LambdaQueryWrapper<UserVocBoundStatisticsDO> vocBoundStatisticsQueryWrapper = new LambdaQueryWrapper<>();
        LambdaUpdateWrapper<UserVocBoundStatisticsDO> vocBoundStatisticsUpdateWrapper = new LambdaUpdateWrapper<>();

        vocBoundStatisticsQueryWrapper.eq(UserVocBoundStatisticsDO::getUserId, userId);
        UserVocBoundStatisticsDO userVocBoundStatisticsDO = userVocBoundStatisticsMapper.selectOne(vocBoundStatisticsQueryWrapper);
        vocBoundStatisticsUpdateWrapper.eq(UserVocBoundStatisticsDO::getUserId, userId)
                .set(UserVocBoundStatisticsDO::getFree, userVocBoundStatisticsDO.getFree() + 1)
                .set(UserVocBoundStatisticsDO::getTotal, userVocBoundStatisticsDO.getTotal() + 1)
                .set(UserVocBoundStatisticsDO::getAvailable, userVocBoundStatisticsDO.getAvailable() + 1);
        userVocBoundStatisticsMapper.update(null, vocBoundStatisticsUpdateWrapper);
    }
    // 更新用户等级
    @Override
    @Transactional
    public void updateUserLevel(Long userId) {
        LambdaQueryWrapper<UserCheckinStatisticsDO> statisticsQueryWrapper = new LambdaQueryWrapper<>();
        LambdaUpdateWrapper<UserDO> userUpdateWrapper = new LambdaUpdateWrapper<>();
        LevelDO targetLevel;
        // 获取用户签到数据
        UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectOne(statisticsQueryWrapper.eq(UserCheckinStatisticsDO::getUserId, userId));
        // 查询所有等级
        List<LevelDO> allLevels = levelMapper.selectList(null);
        // 获取等级
        LevelDO level = getLevel(allLevels, userCheckinStatisticsDO);
        userUpdateWrapper.eq(UserDO::getId,userId)
                .set(UserDO::getLevelName,level.getLevelName())
                .set(UserDO::getLevelValue,level.getLevelValue());
        userMapper.update(null,userUpdateWrapper);
    }
    // 获取等级
    private LevelDO getLevel(List<LevelDO> allLevels, UserCheckinStatisticsDO userCheckinStatisticsDO) {
        int size = allLevels.size();
        allLevels.sort((o1, o2) ->o2.getLevelValue()- o1.getLevelValue());
        for (LevelDO level : allLevels) {
            boolean point=false;
            if (level.getCheckInType()==1){
               point= userCheckinStatisticsDO.getCurContinuousDays()>= level.getCheckInDays();
            }else if (level.getCheckInType()==2){
                point= userCheckinStatisticsDO.getTotalDays()>= level.getCheckInDays();
            }
            if (point){
                return level;
            }
        }
        return allLevels.get(size);
    }

    // APP端获取用户打卡数据
    @Override
    public UserCheckinStatisticsDTO getUserCheckinStatistics(Long userId) {

        LambdaQueryWrapper<UserCheckinStatisticsDO> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(UserCheckinStatisticsDO::getUserId, userId);
        UserCheckinStatisticsDO userCheckinStatisticsDO = userCheckinStatisticsMapper.selectOne(queryWrapper);
        return userCheckinConverter.statisticsDoToDto(userCheckinStatisticsDO);
    }

    /*
             APP端获取用户签到统计数据，注意:
             1. 连续签到是指从今天或者昨天(今天可能尚未打卡)向前计算的连续打卡天数
             2. 如果用户最近一次的连续签到不是今天或者昨天，那么最近连续签到天数为0
         */
    @Override
    public UserCheckinStatisticsDTO getUserContinuousCheckinInfo(Long userId) {
        return null;
    }

    // APP端使用补签卡
    @Override
    public void userResignCard(Long userId) {

    }
}
